#define LOG_NDEBUG 0
#define LOG_TAG "NetIPCP"

#include "ap/NetIPCP.h"

#include <ipcp/ipcp.h>
#include <ipcp/packet.h>
#include "VHalLog.h"
#include <transfer/SafeQueue.h>
#include <transfer/SignalStorage.h>
#include <transfer/utils.h>

#include <set>
#include <thread>

#include "common.h"
#include "convertCBToIPCPValue.h"
#include "convertFromIPCPValue.h"
#include "convertIPCPValueToPA.h"
#include "convertToIPCPValue.h"
#include "ap/signal_id.h"
#include "validate.h"
#include "vp/ApVpPulse.h"
#include "vp/SHAREELEMENT.h"
#include "vp/SignalServiceId.h"
#include "vp/pafunctype.h"
#include <android-base/properties.h>

#define INVALID_SERVICE_ID 0x0

#define SEND_RETRY_MAX_COUNT 3
#define PACKET_HEADER_OFFSET IPCP_LEAST_LEN
#define RETRY_TRANSPORT_CONF_MAX_COUNT 5

namespace ecarx::transfer::ipcp {

static utils::ConcurrentQueue<ecarx::transfer::ipcp::IpcpValue> mIpcpQueue;
static utils::BatchingConsumer<ecarx::transfer::ipcp::IpcpValue> mBatchingConsumer;

static std::set<uint16_t> packageReceived;

template <typename T, size_t N = sizeof(T)>
std::vector<uint8_t> toBytes(T value) {
    std::vector<uint8_t> data;
    data.resize(N, 0);
    memcpy(data.data(), &value, N);
    return data;
}

class IPCPConn::NetIPCP {
  public:
    NetIPCP() {
        mState = NetState::INIT;
        for(auto id:validate::getReadyServiceIds){
            packageReceived.insert(id);
        }
        mBatchingConsumer.run(&mIpcpQueue,
                              std::bind(&NetIPCP::readThread, this, std::placeholders::_1));
        mIPCPCallbacks = new ipcp_callbacks_t;
        mIPCPCallbacks->ipcp_received_cb = netReceived;
        mIPCPCallbacks->error_cb = error_cb;
        initTransport_conf();
        VHALOG.d("ipcp init construct");
    }

    ~NetIPCP() {
        VHALOG.d("ipcp release");
        mBatchingConsumer.requestStop();
        mIpcpQueue.deactivate();
        // We have to wait until consumer thread is fully stopped because it may
        // be in a state of running callback (onBatchHalEvent).
        mBatchingConsumer.waitStopped();
        close();
    }

    //打开链接
    bool open() {
        notifyNetState(NetState::Open);
        VHALOG.d("dbgstart jdbg APVP isDebugMode:%d vp:%s ihu:%s", ecarx::transfer::ipcp::IsDebugMode(),
            ecarx::transfer::ipcp::GetVPIP(), ecarx::transfer::ipcp::GetAPIP());
        bool isTransportConfErr = false;
        int retryTransportConfCount = 0;
        ipcp_stack_err_t error_code = STACK_START_SUCCESS;
        do {
            for (const ipcp_transport_conf_t conf : mTransportConfigs) {
                ::ecarx::transfer::validate::ValidOperationArray operIdsArray =
                    ecarx::transfer::validate::getValidOperation(conf.service_id);
                error_code = ipcp_setup_with_opids(conf, mIPCPCallbacks, operIdsArray.oper_ids,
                                                   operIdsArray.len);
                if (error_code != 0) {
                    VHALOG.e("funtion: %s, ipcp_setup_with_opids service id %d ,failed, errno=%d",
                          __FUNCTION__, conf.service_id, error_code);
                    if (error_code == STACK_FAILURE_CREATE_SOCKET ||
                        error_code == STACK_FAILURE_SET_VLAN ||
                        error_code == STACK_FAILURE_BIND_SOCKET ||
                        error_code == STACK_FAILURE_LISTEN_SOCKET ||
                        error_code == STACK_FAILURE_ACCEPT_SOCKET ||
                        error_code == STACK_FAILURE_CONNECT_SOCKET ||
                        error_code == STACK_FAILURE_CREATE_THREAD ||
                        error_code == STACK_FAILURE_PARM_INVALID) {
                        VHALOG.e("funtion: %s, isTransportConfErr is true", __FUNCTION__);
                        isTransportConfErr = true;
                        break;
                    }
                }
                // VHALOG.d("function: %s, setup service done!", __func__);
            }
            if (isTransportConfErr) {
                ipcp_cleanup();
                std::this_thread::sleep_for(std::chrono::milliseconds(500));
                retryTransportConfCount++;
                if (retryTransportConfCount > RETRY_TRANSPORT_CONF_MAX_COUNT) {
                    break;
                }
                VHALOG.e("funtion: %s, ipcp_setup_with_opids() err[%d], cleanup and retry(%d/%d)",
                      __FUNCTION__, error_code, retryTransportConfCount,
                      RETRY_TRANSPORT_CONF_MAX_COUNT);
            }
        } while (isTransportConfErr);
        openState = error_code == STACK_START_SUCCESS;
        if (openState) {
            VHALOG.d("dbgstart open ipcp success");
            getAll();
        } else {
            VHALOG.e("dbgstart open ipcp failed");
        }
        return openState;
    }
    //关闭链接
    bool close() {
        notifyNetState(NetState::Close);
        ipcp_cleanup();
        return true;
    }

    void syncWithMCU() {
        getAll();
    }

    //将数据写到网络上
    bool write(const Signal& signal) {
        ecarx::transfer::ipcp::IpcpValue ipValue;
        if (ecarx::transfer::utils::isWriteableSignal(signal.signalId)) {
            ipValue = ecarx::transfer::utils::toIpcpValue(signal);
        } else if (ecarx::transfer::utils::isWriteableCBPASignal(signal.signalId)) {
            ipValue = ecarx::transfer::utils::CBToIpcpValue(signal);
        }

        ipcp_packet_t* packet =
            build_ipcp_packet(ipValue.service_id, ipValue.oper_id, ipValue.oper_type,
                              ipValue.payload_type, ipValue.payload, ipValue.payload_len);
        if (ipValue.payload_len > 0) {
            delete[](uint8_t*) ipValue.payload;  // release asn1 payload buffer, ipcp stack
                                                     // already copy buffer to itself.
        }
        if (packet != NULL) {
            ipcp_peer_t peer;
            memset(peer.peer_ip, 0, sizeof(peer.peer_ip));
            strcpy(peer.peer_ip, ipValue.peer_ip);  // service ip
            peer.peer_port = ipValue.peer_port;
            peer.sk_type = IPCP_TRANSPORT_UDP;
            ipcp_stack_err_t err = STACK_SEND_MSG_SUCCESS;
            if ((err = ipcp_send(peer, packet)) != STACK_SEND_MSG_SUCCESS) {
                VHALOG.e("funtion: %d, ipcp_send err : %d", signal.signalId, err);
                return false;
            }
            return true;
        } else {
            VHALOG.e("funtion: %d, build packet failed is not writeable", signal.signalId);
        }
        return false;
    }
    const std::optional<Signal> read(const int32_t signalId) const {
        return mAPVPStorage.get(signalId);
    }

    NetState getState(){
        return mState;
    }

    void notifyNetState(NetState state) {
        mState = state;
        if (mStateCallback != nullptr) {
            mStateCallback(state);
        }
    }

    void onSignalChanged(SignalChangeCallback&& callback) { mSignalChangeCallback = callback; }

    void registerStateHandler(NetStateCallback&& callback) { mStateCallback = callback; }

    //接受网络上的数据回调
    static void netReceived(ipcp_packet_t* packet, ipcp_peer_t peer) {
        std::size_t payload_len = packet->header.length - PACKET_HEADER_OFFSET;
        if (payload_len > 0) {
            ecarx::transfer::ipcp::IpcpValue value;
            value.service_id = packet->header.service_id;
            value.oper_id = packet->header.operation_id;
            value.oper_type = packet->header.op_type;
            value.payload_type = packet->header.payload_type;
            if(packet->header.op_type == IPCP_OPERATION_ERROR){
                VHALOG.e("apvp service id %d opid %d rcv ERROR msg, ignore!", packet->header.service_id,
                  packet->header.operation_id);
                return;
            }
            // service ip
            memset(value.peer_ip, 0, sizeof(value.peer_ip));
            strcpy(value.peer_ip, peer.peer_ip);
            // service port
            value.peer_port = peer.peer_port;
            // payload
            value.payload_len = payload_len;
            value.payload = (void*)new uint8_t[value.payload_len];
            memcpy(value.payload, packet->payload, value.payload_len);
            VHALOG.d("funtion: %s, service id %d opid %d ", __FUNCTION__, packet->header.service_id,
                  packet->header.operation_id);
            mIpcpQueue.push(std::move(value));
        }
    }
    static void error_cb(ipcp_packet_t* ipcp_packet, ipcp_stack_err_t error) {
        VHALOG.d("funtion: %s, service id %d opid %d ipcp stack error : %d", __FUNCTION__,
              ipcp_packet->header.service_id, ipcp_packet->header.operation_id, error);
    }

  private:
    void initTransport_conf() {
        const char* apip = ecarx::transfer::ipcp::GetAPIP();
        for (int id = MIN_SIGNAL_SERVICE_ID; id < MAX_CUSTOM_SERVICE_ID; id++) {
            if (ecarx::transfer::validate::availableServiceID(id)) {
                ipcp_transport_conf_t cfg = {
                                            .service_id = static_cast<uint16_t>(id),
                                            .host_port = MY_LOCAL_PORT,
                                            .sk_type = IPCP_TRANSPORT_UDP,
                                            .vlan = MY_VLAN_ID,
                                            .priority = MY_VLAN_PRIORITY };
                strcpy(cfg.host_ip, apip);
                mTransportConfigs.emplace_back(cfg);
            }
        }
    }
    void readThread(const ecarx::transfer::ipcp::IpcpValue& value) {
        static bool ready = false;
        std::vector<int32_t> changed;
        if (value.service_id < MAX_SIGNAL_SERVICE_ID ||
            value.service_id == EcarXSignalServiceID::serviceID_CARCONFIG ||
            value.service_id == EcarXSignalServiceID::serviceID_CARCONFIGTHOUSAND) {
            // 信号
            std::vector<ecarx::transfer::Signal> buffer = ecarx::transfer::utils::ipcpToSignals(value);
            mAPVPStorage.put(buffer,changed);
        } else {
            // pa
            std::vector<ecarx::transfer::Signal> buffer =
                ecarx::transfer::utils::ipcpToPASignals(value);
            mAPVPStorage.put(buffer,changed);
        }
        if (!ready) {
            packageReceived.erase(value.service_id);
            if (packageReceived.empty()) {
                ready = true;
                VHALOG.d("dbgstart apvp is ready now!");
                notifyNetState(NetState::Ready);
            }
        }
        if (!changed.empty()) {
            mSignalChangeCallback(changed);
        }
        delete[](uint8_t*) value.payload;

        
    }

    void getAll() {
        const char* vpip = ecarx::transfer::ipcp::GetVPIP();
        for(auto id:validate::getAllServiceIds){
            ::ecarx::transfer::validate::ValidOperationArray operIdsArray =
                ecarx::transfer::validate::getValidOperation(id);
            if(operIdsArray.len > 0){
                ipcp_packet_t* packet = build_ipcp_packet(
                    id, 2577, IPCP_OPERATION_SETREQUEST_NORETURN, IPCP_DATA_NORMAL, NULL, 0);
                if (packet != NULL) {
                    ipcp_peer_t peer;
                    memset(peer.peer_ip, 0, sizeof(peer.peer_ip));
                    strcpy(peer.peer_ip, vpip);  // service ip
                    peer.peer_port = SERVICE_PORT;
                    peer.sk_type = IPCP_TRANSPORT_UDP;
                    ipcp_stack_err_t err = STACK_SEND_MSG_SUCCESS;
                    if ((err = ipcp_send(peer, packet)) != STACK_SEND_MSG_SUCCESS) {
                        VHALOG.e("funtion: %s,service id %d ipcp_send err : %d", __FUNCTION__, id,
                              err);
                    }
                } else {
                    VHALOG.e("funtion:%s , build service Id %d packet failed is not readable",
                          __FUNCTION__, id);
                }
            }
        }
    }

    void sendPluseMsg() {
        static int32_t count = 0;
        InterfaceVersion version;
        if (count == 255) {
            count = 0;
        }
        version.counter = count;
        version.version = rpcversion;
        auto bytes = toBytes(version);
        Signal versionSignal;
        versionSignal.signalId = (uint32_t)::ecarx::transfer::CBSignal::CB_ApVp_InterfaceVersion;
        versionSignal.value.bytes = bytes;
        count++;
        write(versionSignal);
    }

  private:
    std::unique_ptr<std::thread> mReadThread;

    SignalChangeCallback mSignalChangeCallback;
    NetStateCallback mStateCallback;

    ipcp_callbacks_t* mIPCPCallbacks;

    std::vector<ipcp_transport_conf_t> mTransportConfigs;
    storage::SignalPool mAPVPStorage;
    NetState mState;
    bool openState = false;
};

IPCPConn::IPCPConn() : mImpl(new NetIPCP()), mIsExitStrMode(true) {}

IPCPConn::~IPCPConn() {
    delete mImpl;
}

bool IPCPConn::open() {
    return mImpl->open();
}

bool IPCPConn::close() {
    return mImpl->close();
}

bool IPCPConn::write(const Signal& signal) {
    return mImpl->write(signal);
}

const std::optional<Signal> IPCPConn::read(const int32_t signalId) const {
    return mImpl->read(signalId);
}

NetState IPCPConn::getState(){
    return mImpl->getState();
}

void IPCPConn::onSignalChanged(SignalChangeCallback&& callback) {
    mImpl->onSignalChanged(std::move(callback));
}

void IPCPConn::registerStateHandler(NetStateCallback&& callback) {
    mImpl->registerStateHandler(std::move(callback));
}

bool IPCPConn::enterStrMode() {
    mIsExitStrMode = false;
    return true;
}

bool IPCPConn::exitStrMode() {
    // 首次初始化类时，默认值为True，不需要再次getAll，如果进入STR在退出STR模式，才需要重新getAll
    if (!mIsExitStrMode) {
        mIsExitStrMode = true;
        mImpl->syncWithMCU();
    }
    return true;
}

}  // namespace ecarx::transfer::ipcp
